
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h> // fork
#include <errno.h>
#include <time.h>

#include <cedule/log.h>

#include "config.h"
#include "server.h"
#include "client.h"

Server_t * server_create( Config_t * config ) {
	Server_t * server;

	server = (Server_t *) malloc (sizeof( Server_t ) );
	if ( !server ) goto error;

	bzero( &(server->addr), sizeof(server->addr) );
	server->config = config;
	server->addr.sin_port = htons( config->port );
	server->addr.sin_family = AF_INET;
	server->addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server->clients = NULL;

	return server;

error:
	return NULL;
}

int server_add_client(  Server_t * server, int clientfd ){
	Client_t * client = NULL;

	client = client_create( clientfd );
	if ( ! client ) goto error;

	if ( server->clients ) {
		// insert at head
		client->next = server->clients;
		server->clients = client;
	} else {
		// set as head
		client->next = NULL;
		server->clients = client;
	}

	return 0;

error:
	return -1;
}

int server_remove_client( Server_t * server, Client_t * client ) {
	client_destroy( client );
	return 0;
}

void server_run( Server_t * server ) {

	int client_socket;
	struct sockaddr_in client_addr;
	unsigned addr_len;
	int err;
	unsigned int yes;
	struct timeval timeout;
	int nbevents;
	Client_t * client = NULL;
	Client_t * tmp_client = NULL;
	time_t timediff;

	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	server->fd = socket( AF_INET,SOCK_STREAM, 0 );
	if ( server->fd < 0 )
	{
		perror( "Unable to create socket" );
		goto error;
	}

	/* allow multiple sockets to use the same PORT number */
	yes = 1;
	if ( setsockopt( server->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes) ) < 0 )
	{
		log_error( "Reusing ADDR failed: %s\n", strerror( errno ) );
		goto error;
	}


	log_info( "server started on port %d\n", server->config->port );
	err = bind( server->fd, (struct sockaddr *)&(server->addr), sizeof(server->addr) );
	if ( err < 0 ){
		log_error ("Unable to bind on port %d\n", server->config->port );
		goto error;
	}
	listen( server->fd, 5 );

	/* accept la connexion */
	addr_len = sizeof( client_addr );

	fd_set masterfd, readfd;
	FD_ZERO( &masterfd );
	FD_SET( server->fd, &masterfd );

	while(1)
	{

		timediff = time(NULL);
		readfd = masterfd;
		nbevents = select( FD_SETSIZE, &readfd, NULL, NULL, &timeout );
		timediff = time(NULL) - timediff;

		if (nbevents < 0 ){
			perror("select");
		}

		if ( FD_ISSET( server->fd, &readfd ) ) {
			client_socket = accept( server->fd,
					(struct sockaddr *)&client_addr,
					&addr_len);
			FD_SET( client_socket, &masterfd );
			log_info( "Accepted connection\n" );

			server_add_client( server, client_socket );
		}

		client = server->clients;
		while( client ) {
			if ( FD_ISSET( client->fd, &readfd ) ) {
				// current client received something
				client_run( client );
			} else {
				// current client has nothing
				if ( client->idle > CLIENT_IDLE_MAX ) {
					FD_CLR( client->fd, &masterfd );
					tmp_client = client->next;
					server_remove_client( server, client );
					client = tmp_client;
					continue;
				} else {
					//FIXME: use real idletime...
					client->idle += timediff;
				}
				// remove client if needed
			}

			client = client->next;
		}
	}
	shutdown( server->fd, 2 );
	close( server->fd );
	return ;

error:
	close( client_socket );
	exit( EXIT_FAILURE );
}

